"""Core data models for the SwiftShadow application, using SQLAlchemy ORM.

This module defines the SQLAlchemy declarative base and various ORM models
such as `DownloadTask`, `SiteProfile`, `GlobalConfig`, `ProxyServer`, and
`DownloadHistory`. It also includes Pydantic models for API request/response
validation and several enumeration types for task status, priority, and proxy protocols.
"""

from datetime import datetime
from enum import Enum
from typing import Any
from uuid import uuid4

from pydantic import BaseModel, Field
from sqlalchemy import Enum as SQLEnum
from sqlalchemy import JSON, Column, DateTime, Float, ForeignKey, Integer, String, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

# SQLAlchemy Base
Base = declarative_base()


# ==================== 枚举类型 ====================


class TaskStatus(str, Enum):
    """Enumeration for the possible statuses of a download task.

    Attributes:
        PENDING: The task is waiting to be started.
        DOWNLOADING: The task is currently actively downloading.
        PAUSED: The task is temporarily suspended.
        COMPLETED: The task has finished successfully.
        FAILED: The task encountered an error and could not complete.
        CANCELLED: The task was stopped by the user.
    """
    PENDING = "pending"
    DOWNLOADING = "downloading"
    PAUSED = "paused"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


class TaskPriority(int, Enum):
    """Enumeration for the priority levels of a download task.

    Attributes:
        LOW: Lowest priority.
        MEDIUM: Medium priority.
        HIGH: Highest priority.
    """
    LOW = 0
    MEDIUM = 1
    HIGH = 2


class ProxyProtocol(str, Enum):
    """Enumeration for supported proxy protocols.

    Attributes:
        HTTP: HTTP proxy protocol.
        HTTPS: HTTPS proxy protocol.
        SOCKS5: SOCKS5 proxy protocol.
    """


class FileType(str, Enum):
    """Enumeration for the type of file being downloaded.

    Attributes:
        VIDEO: The file is a video.
        AUDIO: The file is an audio.
        OTHER: The file is neither video nor audio.
    """
    VIDEO = "video"
    AUDIO = "audio"
    OTHER = "other"


# ==================== SQLAlchemy ORM模型 ====================


class DownloadTask(Base):
    """SQLAlchemy ORM model representing a single download task.

    This model stores all relevant information about a download, including its URL,
    status, progress, file details, network configurations, and metadata.
    """

    __tablename__ = "download_tasks"

    # 基础字段
    id = Column(String(36), primary_key=True, default=lambda: str(uuid4()))
    url = Column(String(2048), nullable=False, index=True)
    title = Column(String(512), nullable=False)
    filename = Column(String(512), nullable=False)
    save_path = Column(String(2048), nullable=False)
    file_size = Column(Integer, nullable=True)
    file_type = Column(SQLEnum(FileType), default=FileType.OTHER, nullable=False)

    # 状态字段
    status = Column(SQLEnum(TaskStatus), default=TaskStatus.PENDING, nullable=False, index=True)
    priority = Column(Integer, default=TaskPriority.MEDIUM.value, nullable=False)
    progress = Column(Float, default=0.0)
    downloaded_bytes = Column(Integer, default=0)
    speed = Column(Float, default=0.0)

    # 时间字段
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    started_at = Column(DateTime, nullable=True)
    completed_at = Column(DateTime, nullable=True)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

    # 配置字段
    format_id = Column(String(50), nullable=True)
    quality = Column(String(50), nullable=True)
    parallel_threads = Column(Integer, default=4)
    retry_count = Column(Integer, default=0)
    max_retries = Column(Integer, default=3)

    # 网络配置
    proxy_url = Column(String(512), nullable=True)
    cookies = Column(Text, nullable=True)  # 加密后的Cookie
    user_agent = Column(String(512), nullable=True)
    referer = Column(String(2048), nullable=True)
    custom_headers = Column(JSON, default=dict)

    # 元数据字段
    thumbnail_url = Column(String(2048), nullable=True)
    duration = Column(Integer, nullable=True)
    uploader = Column(String(256), nullable=True)
    description = Column(Text, nullable=True)
    extra_metadata = Column(JSON, default=dict)

    # 错误信息
    error_message = Column(Text, nullable=True)

    # 外键
    site_profile_id = Column(String(36), ForeignKey("site_profiles.id"), nullable=True)

    # 关系
    site_profile = relationship("SiteProfile", back_populates="tasks")

    def to_dict(self) -> dict[str, Any]:
        """Converts the DownloadTask object to a dictionary.

        Returns:
            dict[str, Any]: A dictionary representation of the task.
        """
        return {
            "id": self.id,
            "url": self.url,
            "title": self.title,
            "filename": self.filename,
            "save_path": self.save_path,
            "file_size": self.file_size,
            "status": self.status.value if isinstance(self.status, TaskStatus) else self.status,
            "priority": self.priority,
            "progress": self.progress,
            "downloaded_bytes": self.downloaded_bytes,
            "speed": self.speed,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "started_at": self.started_at.isoformat() if self.started_at else None,
            "completed_at": self.completed_at.isoformat() if self.completed_at else None,
            "updated_at": self.updated_at.isoformat() if self.updated_at else None,
            "format_id": self.format_id,
            "quality": self.quality,
            "parallel_threads": self.parallel_threads,
            "retry_count": self.retry_count,
            "max_retries": self.max_retries,
            "thumbnail_url": self.thumbnail_url,
            "duration": self.duration,
            "uploader": self.uploader,
            "error_message": self.error_message,
        }


class SiteProfile(Base):
    """SQLAlchemy ORM model representing a site-specific download profile.

    This model stores configurations for specific websites, such as custom headers,
    cookies, user agents, and other settings that might be required for downloading
    content from certain sites.
    """

    __tablename__ = "site_profiles"

    def __init__(self, name, url_pattern, headers, cookies, user_agent, download_directory, max_connections,
                 retry_attempts, enable_js_rendering, notes):
        """Initializes a new SiteProfile instance.

        Args:
            name: The name of the site profile.
            url_pattern: The URL pattern this profile applies to.
            headers: Custom HTTP headers for requests.
            cookies: Custom cookies for requests.
            user_agent: Custom user agent string.
            download_directory: Default download directory for this profile.
            max_connections: Maximum concurrent connections for downloads.
            retry_attempts: Number of retry attempts for failed downloads.
            enable_js_rendering: Whether to enable JavaScript rendering for this site.
            notes: Additional notes for the site profile.
        """

    id = Column(String(36), primary_key=True, default=lambda: str(uuid4()))
    name = Column(String(256), nullable=False)
    domain_pattern = Column(String(512), unique=True, nullable=False, index=True)

    # 下载配置
    parallel_threads = Column(Integer, default=4)
    max_speed = Column(Integer, nullable=True)
    preferred_format = Column(String(50), nullable=True)
    preferred_quality = Column(String(50), nullable=True)

    # 网络配置
    proxy_url = Column(String(512), nullable=True)
    cookies = Column(Text, nullable=True)
    user_agent = Column(String(512), nullable=True)
    referer = Column(String(2048), nullable=True)
    custom_headers = Column(JSON, default=dict)

    # 文件管理
    save_directory = Column(String(2048), nullable=True)
    filename_template = Column(String(512), default="%(title)s-%(id)s.%(ext)s")

    # 时间
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)

    # 关系
    tasks = relationship("DownloadTask", back_populates="site_profile")


class GlobalConfig(Base):
    """SQLAlchemy ORM model for storing global application configurations.

    This model holds application-wide settings such as default download paths,
    concurrent download limits, and other general preferences.
    """

    __tablename__ = "global_config"

    def __init__(self, id=None, default_download_path=None, max_concurrent_downloads=None, enable_dark_mode=None,
                 language=None,
                 notification_settings=None, proxy_settings=None, **kwargs):
        if id is not None:
            self.id = id
        super().__init__(**kwargs)
        """Initializes a new GlobalConfig instance.

        Args:
            default_download_path: The default path for saving downloads.
            max_concurrent_downloads: Maximum number of simultaneous downloads.
            enable_dark_mode: Whether dark mode is enabled.
            language: The application's display language.
            notification_settings: JSON string of notification preferences.
            proxy_settings: JSON string of global proxy settings.
        """

    id = Column(Integer, primary_key=True, default=1)

    # 下载设置
    max_concurrent_downloads = Column(Integer, default=3)
    default_save_path = Column(String(2048), default="./downloads")
    default_threads = Column(Integer, default=4)
    enable_auto_resume = Column(Integer, default=1)  # SQLite用0/1代替bool

    # 速度限制
    global_speed_limit = Column(Integer, nullable=True)
    enable_idle_mode = Column(Integer, default=0)
    idle_threshold_minutes = Column(Integer, default=10)

    # 网络设置
    default_proxy = Column(String(512), nullable=True)
    timeout = Column(Integer, default=30)

    # 自动化
    auto_start_downloads = Column(Integer, default=1)
    enable_post_processing = Column(Integer, default=1)
    shutdown_after_completion = Column(Integer, default=0)

    # UI设置
    language = Column(String(10), default="zh_CN")
    theme = Column(String(20), default="auto")
    enable_system_tray = Column(Integer, default=1)
    minimize_to_tray = Column(Integer, default=1)

    # 隐私
    encrypt_cookies = Column(Integer, default=1)
    clear_history_on_exit = Column(Integer, default=0)

    # 时间
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)


class ProxyServer(Base):
    """SQLAlchemy ORM model for storing proxy server configurations.

    This model allows defining and managing various proxy servers (HTTP, HTTPS, SOCKS5)
    that can be used for downloading tasks, including authentication details.
    """

    __tablename__ = "proxy_servers"

    def __init__(self, name, protocol, host, port, username, password, is_enabled):
        """Initializes a new ProxyServer instance.

        Args:
            name: The name of the proxy server.
            protocol: The protocol used by the proxy (e.g., HTTP, SOCKS5).
            host: The hostname or IP address of the proxy server.
            port: The port number of the proxy server.
            username: Username for proxy authentication (if required).
            password: Password for proxy authentication (if required).
            is_enabled: Whether the proxy server is currently enabled.
        """

    id = Column(String(36), primary_key=True, default=lambda: str(uuid4()))
    name = Column(String(256), nullable=False)
    protocol = Column(SQLEnum(ProxyProtocol), nullable=False)
    host = Column(String(256), nullable=False)
    port = Column(Integer, nullable=False)
    username = Column(String(256), nullable=True)
    password = Column(Text, nullable=True)  # 加密存储

    # 测试结果
    is_active = Column(Integer, default=1)
    latency_ms = Column(Integer, nullable=True)
    last_tested_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)

    @property
    def url(self) -> str:
        """Constructs the proxy URL string.

        Returns:
            str: The full proxy URL.
        """
        protocol = (
            self.protocol.value if isinstance(self.protocol, ProxyProtocol) else self.protocol
        )
        if self.username and self.password:
            return f"{protocol}://{self.username}:{self.password}@{self.host}:{self.port}"
        return f"{protocol}://{self.host}:{self.port}"


class DownloadHistory(Base):
    """SQLAlchemy ORM model for storing historical download records.

    This model tracks completed or failed download tasks, providing a record of
    past download activities, including timestamps and final status.
    """

    __tablename__ = "download_history"

    def __init__(self, task_id, url, file_name, save_path, total_size, downloaded_size, status, start_time, end_time,
                 error_message):
        """Initializes a new DownloadHistory instance.

        Args:
            task_id: The ID of the associated download task.
            url: The URL of the downloaded file.
            file_name: The name of the downloaded file.
            save_path: The local path where the file was saved.
            total_size: The total size of the file in bytes.
            downloaded_size: The final downloaded size in bytes.
            status: The final status of the download task.
            start_time: The timestamp when the download started.
            end_time: The timestamp when the download ended.
            error_message: Any error message if the download failed.
        """

    id = Column(String(36), primary_key=True, default=lambda: str(uuid4()))
    task_id = Column(String(36), ForeignKey("download_tasks.id"), nullable=False, index=True)
    source_url = Column(String(2048), nullable=False)
    direct_url = Column(Text, nullable=False)
    extracted_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    expires_at = Column(DateTime, nullable=True)
    is_valid = Column(Integer, default=1)


# ==================== Pydantic模型(用于验证和API) ====================


class TaskCreate(BaseModel):
    """Pydantic model for creating a new download task.

    This model defines the required and optional fields when a client requests
    to create a new download task, ensuring data validation.
    """

    url: str = Field(..., min_length=1, max_length=2048)
    title: str | None = None
    filename: str | None = None
    save_path: str | None = None
    quality: str | None = None
    priority: TaskPriority = TaskPriority.MEDIUM
    proxy_url: str | None = None
    user_agent: str | None = None
    referer: str | None = None
    file_type: FileType = FileType.OTHER

    class Config:
        """Pydantic configuration for TaskCreate model.

        Attributes:
            json_encoders: Custom JSON encoders for specific types.
            arbitrary_types_allowed: Allows arbitrary types for fields.
        """
        json_encoders = {
            datetime: lambda v: v.isoformat(),
            TaskStatus: lambda v: v.value,
            TaskPriority: lambda v: v.value,
        }
        arbitrary_types_allowed = True


class TaskResponse(BaseModel):
    """Pydantic model for returning download task details.

    This model defines the structure of the data returned to the client when
    querying for download task information, including all relevant task attributes.
    """

    id: str
    url: str
    title: str
    filename: str
    save_path: str
    status: str
    progress: float
    speed: float
    file_type: FileType
    created_at: str | None = None

    class Config:
        """Pydantic configuration for TaskResponse model.

        Attributes:
            json_encoders: Custom JSON encoders for specific types.
            arbitrary_types_allowed: Allows arbitrary types for fields.
            from_attributes: Enables ORM mode for automatic field mapping.
        """
        from_attributes = True
